多线程创建方式及线程安全问题
1.创建线程方式
一: 创建线程方式一继承Thread类
public clsss MyThread extends Thread{
//重写run方法,设置线程任务
Run(){
}
}
main(){
new MyThread().start();
}
获取线程名称:
Thread.currentThread()获取当前线程对象
Thread.currentThread().getName();获取当前线程对象的名称
创建线程的步骤。
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
l 代码演示:
public class Demo02 {
public static void main(String[] args) {
//创建线程执行目标类对象
Runnable runn = new MyRunnable();
//将Runnable接口的子类对象作为参数传递给Thread类的构造函数
Thread thread = new Thread(runn);
Thread thread2 = new Thread(runn);
//开启线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程:正在执行!"+i);
}
}
三:两种方法的使用比较
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
第一种方式继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,又有线程任务。
实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
2 线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程不安全:
1. 单线程没有线程安全问题
2. 多线程没有使用相同的内容,也会不出现安全问题
3. 多线线程使用了同一个资源,可能会出现线程安全问题
一:使用同步代码块解决线程安全问题
synchronized(锁对象){
可能出现安全问题的代码
(访问了共享数据的代码)
}
同步代码块: 在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
使用同步代码块,对电影院卖票案例中Ticket类进行如下代码修改:
publicclass Ticket implements Runnable {
//共100票
intticket = 100;
//定义锁对象
Object lock = new Object();
@Override
publicvoid run() {
//模拟卖票
while(true){
//同步代码块
synchronized (lock){
if (ticket > 0) {
//模拟电影选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
}
}
注意:保证锁对象要唯一
二:使用同步方法解决线程安全问题
1.把访问共享数据的代码,提取出来放在一个方法中
2.在方法上增加一个同步关键字
修饰符 synchronized 返回值类型 方法名(参数列表){
出现了安全问题的代码
(使用了共享数据的代码)
}
l 同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this
使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改:
publicclass Ticket implements Runnable {
//共100票
intticket = 100;
//定义锁对象
Object lock = new Object();
@Override
publicvoid run() {
//模拟卖票
while(true){
//同步方法
method();
}
}
//同步方法,锁对象this
publicsynchronizedvoid method(){
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
l 静态同步方法: 在方法声明上加上static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class
Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。
我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改:
publicclass Ticket implements Runnable {
//共100票
intticket = 100;
//创建Lock锁对象
Lock ck = new ReentrantLock();
@Override
publicvoid run() {
//模拟卖票
while(true){
//synchronized (lock){
ck.lock();
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
ck.unlock();
//}
}
}
}