Thread类:Java语言通用线程类,其内部用空的run()方法,创建新的线程需要重新run方法,否则不做任何事
线程的启动:调用Thread派生类对象的start()方法,如果直接调用方法,则run方法会同步执行
java实现多线程的两种方法:
-
派生Thread类
class T1 extends Thread { // 重写Thread run方法 run () { // 编写线程任务代码 } } T1 t1 = new T1(); // 创建一个线程对象 t1.start() // 通过start()方法即可执行线程run方法 -
实现Runable接口,解决Thread类无法进行多继承的问题
class T implements Runnable { // 重写run方法 run () { // 编写线程任务代码 } } T t = new T(); // 实例化一个Runable对象 Thread t2 = new Thread(t); // 通过Thread类构造方法 创建线程对象 t2.start(); // 通过线程对象的start方法开启线程
// 状态转变方法 start(); // 开始执行线程 sleep(long); // 睡眠一段时间自动唤醒,毫秒 sleep(long, int); // 睡眠一段时间自动唤醒,精确到纳秒 time = ms + ns wait(); // 进入阻塞状态 等待 notify(); // 唤醒在此对象监视器上等待的线程 如果不知道唤醒哪一个线程 则最好使用notifyAll notifyAll(); // 唤醒在此对象监视器上等待的多个线程 yield(); // 放弃执行 // 不建议使用的状态转变方法,容易造成死锁 stop(); // 关闭线程,释放当前线程的所有对象锁 destroy(); // 关闭线程,不释放对象锁 suspend(); // 挂起执行 可用sleep代替 resume(); // 恢复执行 可用notify代替 // 等待其他线程 isAlive(); // 判断此线程是否处于运行状态,可用于同步 join(); // 如t1.join(),阻塞当前运行此方法的线程,等待t1线程执行结束后,当前线程才执行 // 设置优先级 setPriority(); // 设置优先级,优先级高的线程获取CPU资源的可能性更大 1-10线程同步:
-
多线程并行存在的问题:
假设有两个线程T1,t2进行售票,t1查找数据库,发现火车票T可以出售,所以准备出售T,此时计算机系统切换到t2执行,t2发现火车票T可以出售,因此出售了T,系统再次切换回t1时,t1又会将火车票T出售,这样会导致出错。
-
synchronized关键字
java提供了锁的机制,原理是每个线程进入同步代码之前,必须获得锁,否则不能进入。这样就解决了多个线程竞争共享代码的情况。java中锁机制的实现方法是在共享代码之前加入synchronized关键字。
java有一个同步模型监视器,为每个具有同步代码的对象准备唯一的一把锁,得到锁的线程可以进入同步代码,其他线程只能在外等待。
如果获得锁的线程调用wait()方法进入阻塞状态,其他线程可以使用notify()方法唤醒该线程
-
同步格式
// 同步方法 public synchronized void method() { ... } // 同步代码快 synchronized(obj) { // 需获得obj对象的锁才能进入,obj作用域不同,控制情况不同 ...//obj可以使用this }
-
场景:消费者和生产者
假设寒冷的冬天,人们在在路边摊买手抓饼,如果老板做出的饼太多没有卖出会凉掉,所以他只做一个饼,等一个饼卖掉了,才会又做一个饼。如果有做好的饼,路人会直接买,如果没有,路人会告诉老板他需要做饼,然后等等老板做好,老板做好了会告诉路人可以买饼了。
-
关键代码
// 同步方法同一个时刻只能一个线程方法 private synchronized void buyFood () { if (Thread.currentThread().getName().equals("T1")){ // 生产者线程 if (food == 0) { food ++; // 输出: "做了一个手抓饼,现在有" + food; notify(); // 做好饼了通知路人 } else { wait(); // 如果还有饼 需要卖出之后再做 } } else { // 消费者线程 if (food > 0) { food --; // 输出: "买一个手抓饼,还剩" + food; } else { notify(); // 没有饼了通知老板做饼 wait(); // 等待老板做饼 } } }死锁 为了保证数据安全使用synchronized关键字实现对象锁的同步机制,但是如果一个线程拥有了A的锁,这时候需要B的锁,而另一个线程拥有B的锁,这时候需要A的锁,这样就出现了死锁的现象,因为两个线程都在等待对象释放锁,所以两个线程会无休止地等待下去,导致程序无法继续往下运行。
// 死锁示例关键代码: private void accessA () { flag = false; // 设置标志位,使另一个线程走进另一个分支 synchronized (A) { // 输出:Thread.currentThread().getName() + "已经获得A的锁"; Thread.sleep(1000); // 休眠 确保另一个线程获得B的锁 // 输出:Thread.currentThread().getName() + "准备获取B的锁..."; synchronized (B) { // 输出:Thread.currentThread().getName() + "已经获得B的锁"; } } } private void accessB () { flag = true; // 设置标志位,使另一个线程走进另一个分支 synchronized (B) { // 输出:Thread.currentThread().getName() + "已经获得B的锁"; Thread.sleep(1000); // 休眠 确保另一个线程获得A的锁 // 输出:Thread.currentThread().getName() + "准备获取A的锁..."; synchronized (A) { // 输出:Thread.currentThread().getName() + "已经获得A的锁"; } } } private class T extends Thread { @Override public void run() { if (flag) { accessA(); } else { accessB(); } } }控制台输出结果:
T1已经获得A的锁
T2已经获得B的锁
T2准备获取A的锁…
T1准备获取B的锁…



