Java基础-多线程总结
线程常用方法
方法 | 说明 |
---|---|
public static void sleep(long millis) | 当前线程主动休眠millis毫秒。 |
public static void yield() | 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。 |
public final void join() | 允许其他线程加入到当前线程中 |
Thread.setDaemon(true) | 守护进程,Thread(线程名),必须在线程启动之前调用 |
Thread.setPriority(int a) | 线程优先级,a为1-10 |
方法就不和大家演示了
如何使用多线程
-
继承Thread类
class MyThread extends Thread{ @Override public void run() { //编写线程中的操作 } }
-
实现Runnable接口
class MyRunnable implements Runnable{ public void run(){ //多线程处理的逻辑代码 } }
推荐使用Runnable
1.因为java中单继承,继承了Thread类就无法在继承其他的类了,使用Runnable接口更为灵活一点;
2.另外继承代表强关联,实现稍微弱一点;
执行线程的是start方法
案例
使用子线程打印 0-9
- 继承Thread
//继承Thread
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//开启子线程
System.out.println("主线程结束");
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<10;i++){
//Thread.currentThread().getName(),当前线程的名字
System.out.println(Thread.currentThread().getName()+"---"+i);
}
System.out.println("子线程结束");
}
}
- 实现Runnable
//实现Runnable
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println("主线程结束");
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i=0;i<10;i++){
//Thread.currentThread().getName(),当前线程的名字
System.out.println(Thread.currentThread().getName()+"---"+i);
}
System.out.println("子线程结束");
}
}
- 其他
public class RunnableDemo1 {
public static void main(String[] args) {
//使用匿名内部类创建线程
new Thread(new Runnable() {
@Override
public void run() {
//执行代码
}
}).start();
}
}
两者运行结果
线程安全问题
-
什么是线程不安全
- 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
- 临界资源:共享资源(同一对象),一次仅允许一个线程,才可以保证其正确性。
- 原子操作:不可分割的多步操作,被视作一个整体其顺序不可打乱或缺省
-
案例:车站有四个窗口共卖100张票
public class Sell2 {
public static void main(String[] args) {
/*车站四个窗口共卖100张票*/
MyRunnable2 runnable = new MyRunnable2();
Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字
Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字
Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字
Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字
//开启线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class MyRunnable2 implements Runnable {
int size = 100;
@Override
public void run() {
while (size>0){
try {
Thread.sleep(100);//让线程睡眠100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
size--;
System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size);
}
}
}
运行结果
- 案例分析
假设窗口一拿到了最后一张票时,还没来得及size–,其他窗口已经进入了while(size>0)循环,所以导致上述问题
解决线程安全问题
-
使用同步代码块
- synchronized(临界资源对象){//原子操作}//(给临界资源加锁)
public class Sell2 { public static void main(String[] args) { /*车站四个窗口共卖100张票*/ MyRunnable2 runnable = new MyRunnable2(); Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字 Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字 Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字 Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字 //开启线程 thread1.start(); thread2.start(); thread3.start(); thread4.start(); } } class MyRunnable2 implements Runnable { int size = 100; @Override public void run() { while (size>0){ try { Thread.sleep(100);//让线程睡眠100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this){ if(size>0){ size--; System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size); } } } } }
确保size>0 和 size–,是一个原子操作
-
使用同步方法
- synchronized 返回值类型 方法名称(形参列表){//原子操作}//对当前对象(this)加锁
public class Sell { public static void main(String[] args) { /*车站四个窗口共卖100张票*/ MyRunnable runnable = new MyRunnable(); Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字 Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字 Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字 Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字 //开启线程 thread1.start(); thread2.start(); thread3.start(); thread4.start(); } } class MyRunnable implements Runnable { int size = 100; @Override public void run() { while (size>0){ try { Thread.sleep(100);//让线程睡眠100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } private synchronized void sell() { if(size>0){ size--; System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size); } } }
不能 将while放入synchronized 修饰的方法里面,会导致只有一个线程工作
-
使用Lock
Lock没有自动获取锁和自动释放锁概念
使用void lock();//获取锁 void unlock();//释放锁
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Sell1 { public static void main(String[] args) { /*车站四个窗口共卖100张票*/ MyRunnable1 runnable = new MyRunnable1(); Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字 Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字 Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字 Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字 //开启线程 thread1.start(); thread2.start(); thread3.start(); thread4.start(); } } class MyRunnable1 implements Runnable { Lock lock = new ReentrantLock(); int size = 100; @Override public void run() { while (size>0){ try { Thread.sleep(100);//让线程睡眠100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } lock.lock();//获取锁 if(size>0){ size--; System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size); } lock.unlock();//释放锁 } } }
- Lock常见的方法
方法 说明 void lock(); 获取锁,锁被占用,则等待 会阻塞 boolean tryLock(); 尝试获取锁,成功返回true,失败false,不阻塞 void unlock(); 释放锁 -
Lock与synchronized比较
synchronized lock 实现 jvm实现 java代码实现 释放锁的时机 1.加锁的代码块执行完毕
2.异常finally中释放锁 线程是否一直等待 一直等待 尝试获取锁,非一直等待 类型 可重入,不可中断,非公平 可重入,可中断,可公平 方式 悲观锁 悲观锁 底层使用了cas -
补充
什么是重入锁
重入锁,就是指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。 (防止死锁)
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class RepetitionLock { public static void main(String[] args) { Lock lock = new ReentrantLock(); new Thread(new Runnable() { @Override public void run() { lock.lock();//获取锁 System.out.println("第一此获取锁"); lock.lock();//再次获取同一个锁对象 System.out.println("第二此获取锁"); lock.unlock();//释放锁 System.out.println("释放第二次获取的锁"); lock.unlock();//释放锁 System.out.println("释放第一次获取的锁"); } }).start(); } }
死锁
一个简单的死锁案例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyDeadlock {
public static void main(String[] args) {
Lock leftLock = new ReentrantLock();//左锁
Lock rightLock = new ReentrantLock();//右锁
new Thread(new Runnable() {
@Override
public void run() {
leftLock.lock();//获取左锁
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"获取到了左锁");
rightLock.lock();//获取右锁
System.out.println(Thread.currentThread().getName()+"获取到了右锁");
rightLock.unlock();
leftLock.unlock();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
rightLock.lock();//获取右锁
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"获取到了右锁");
leftLock.lock();//获取左锁
System.out.println(Thread.currentThread().getName()+"获取到了左锁");
leftLock.unlock();
rightLock.unlock();
}
}).start();
}
}
运行结果:一直在运行
Lock 在这里知识简单的说了一下,大家要是对Lock感兴趣的话可以在多查查这方面的资料