Java-多线程(二)
- 一、匿名内部类
- 二、线程安全问题
- 三、解决线程安全问题方法
- 1.同步代码块
- 2.同步方法
- 3.Lock锁
一、匿名内部类
匿名内部类:简化代码实现线程的创建
格式:
new 父类/接口(){
重写父类或者接口中的方法
}
public class Thread2Demo01 {
public static void main(String[] args) {
//使用一般方法
ThreadDemo01 th1 = new ThreadDemo01();
th1.start();
//使用匿名内部类创建线程
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("匿名对象");
}
}.start();
//使用一般方式
ThreadDemo02 th2 = new ThreadDemo02();
Thread th = new Thread(th2);
th.start();
//使用匿名接口
new Thread(new Runnable(){
public void run() {
System.out.println("匿名接口");
}
}).start();
}
}
class ThreadDemo01 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("匿名对象");
}
}
class ThreadDemo02 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("匿名接口");
}
}
二、线程安全问题
线程安全问题:
当多个线程同时访问共享数据时,会出现重复数据或者不合法的数据
public class Thread2Demo02 {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
//定义一个买火车票的类,实现Runnable接口,实现多线程
class Ticket implements Runnable{
private int ticket = 10;//假设还有十张票
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
ticket--;}
}
}
}
三、解决线程安全问题方法
解决线程安全问题的方式:
1.同步代码块:synchronized关键字可以用于方法的某个区域,表示只对这个区域的资源实现互斥访问
2.同步方法
3.锁机制
1.同步代码块
第一种方式:同步代码块
格式:
synchronized(锁对象){
可能产生线程安全问题的代码()
}
注意事项:
1.synchronized括号内的锁对象可以是任意对象。
2.定义锁对象的语句必须在run方法之外。
3.多个线程使用的锁对象必须是同一个。
4.锁对象保证了只让一个线程在同步代码块中执行。
public class Thread2Demo03 {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
class Ticket1 implements Runnable{
private int ticket = 10;//假设还有十张票
//1.定义一个锁对象,一般情况可以直接使用Object,需要定义在可能产生线程安全的代码之外
Object obj = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//2.使用同步代码块,将可能产生线程安全的代码进行包裹
synchronized(obj){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
ticket--;}
}}
}
}
2.同步方法
解决线程安全问题第二种方式:
同步方法:使用synchronized修饰的方法,叫做同步方法,保证一个线程执行此方法时,其它线程只能处于等待状态。
格式:
修饰符 synchronized 返回值类型 方法名 (参数列表){
可能出现线程安全的代码(即访问了共享数据的代码)
}
注意事项:
同步方法也会锁住方法内部的代码,只让一个线程执行,而同步方法中的锁对象就是this,在本例中也就是 new Ticket1()。
静态的同步方法和同步方法的锁对象不同,this是创建对象之前产生的,而静态方法优先于对象,静态方法的锁对象是本类的class书写,即class文件对象。
格式:
修饰符 static synchronized 返回值类型 方法名 (参数列表){
可能出现线程安全的代码(即访问了共享数据的代码)
}
public class Thread2Demo04 {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
class Ticket2 implements Runnable{
private static int ticket = 10;//假设还有十张票
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(this);
while(true){
buy();
// synchronized(this){//和同步方法作用一致,说明同步方法的锁对象为this
// if(ticket>0){
//
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
// ticket--;}
// }
// synchronized(Ticket2.class){//和同步方法作用一致,说明同步方法的锁对象为this
// if(ticket>0){
//
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
// ticket--;}
// }
}
}
// //使用同步方法
// public synchronized void buy(){
// if(ticket>0){
//
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
// ticket--;}
// }
//使用静态同步方法
public static synchronized void buy(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
ticket--;}
}
}
3.Lock锁
线程安全问题的解决方法:
java.util.concurrent.Lock接口
lock比synchronized更加先进,可以实现更广泛的操作
lock接口的方法:
void lock():获取锁
void unlock():释放锁
因为Lock是接口,不能直接创建对象,所以需要使用实现类ReentrantLock,java.util.concurrent.Locks.ReentrantLock implements Lock接口。
实现方法:
1.在实现类成员位置创建ReentrantLock对象。
2.在可能出现线程安全问题的代码前调用Lock接口中的lock()方法,获取锁。
3.在可能出现线程安全问题的代码后调用Lock接口中的unlock()方法,释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class Thread2Demo05 {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
//定义一个买火车票的类,实现Runnable接口,实现多线程
class Ticket3 implements Runnable{
private int ticket = 10;//假设还有十张票
// 1.在实现类成员位置创建Reentrantlock对象
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//2.在可能出现线程安全问题的代码前调用Lock接口中的lock()方法,获取锁
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket+"票");
ticket--;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{//无论是否执行完成,都释放锁
// 3.在可能出现线程安全问题的代码后调用Lock接口中的unlock()方法,释放锁
lock.unlock();
}
}
}
}
}