多线程实现方式
1. 继承Thread类
使用 Thread 子类创建线程的优点是: 可以在子类中增加新的成员变量, 使线程具有某种属性, 也可以在子类中增加新的方法, 使线程具有某种功能. 但是, Java不支持多继承, Thread 类的子类不能在扩展其他的类.
public class Thread01 {
public static void main(String[] args) {
//实现方式一:继承Thread类
TicketWindow t1 = new TicketWindow();
TicketWindow t2 = new TicketWindow();
TicketWindow t3 = new TicketWindow();
t1.start();
t2.start();
t3.start();
}
}
class TicketWindow extends Thread{
private int tickets = 100;
@Override
public void run() {
while(true){
if(tickets > 0 ){
System.out.println(Thread.currentThread().getName() + "正在发售第 " + tickets-- +"张票.");
}else{
break;
}
}
}
}
2. 实现Runnable接口
避免了Java单继承带来的局限性,把线程代码和任务的代码分离,完全解耦。软件设计的时候遵守原则:低耦合、高内聚。事物和事物之间的依赖程度称为它们的耦合度。适合多个相同程序代码的线程去处理同一个资源的情况, 更灵活的实现数据的共享。
public class Thread02 {
public static void main(String[] args) {
//实现方式二:实现Runnable接口
TicketWindow02 t = new TicketWindow02();
Thread t1 = new Thread(t,"窗口一");
Thread t2 = new Thread(t,"窗口二");
Thread t3 = new Thread(t,"窗口仨");
t1.start();
t2.start();
t3.start();
}
}
class TicketWindow02 implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() + "正在发售第 " + tickets-- +"张票.");
}else{
break;
}
}
}
}
3. 线程池来执行
public class Thread05 {
public static void main(String[] args) {
// 实现方式三:实现Runnable接口
//创建一个 任务类 对象
TicketWindow05 task = new TicketWindow05();
//创建一个线程池对象 初始值
ExecutorService exec = Executors.newFixedThreadPool(3);
//将任务放入线程池中
exec.execute(task);
exec.execute(task);
exec.execute(task);
}
}
class TicketWindow05 implements Runnable {
private int tickets = 100;
//创建一个锁对象
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
this.sellTicket();
if (tickets <= 0) {
break;
}
}
}
public void sellTicket() {
// 获取锁
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第 " + tickets-- + " 张票.");
}
// 释放锁
lock.unlock();
}
}
解决线程安全问题
上面的三种方式,除了线程池,如果启动多线程,那么很容易出现线程安全问题.也就是说每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。就是当多个线程同时访问临界资源时,就可能会产生线程安全问题.
解决方式一:同步代码块
使用synchronized(Object obj){}将有可能会发生安全问题的代码包起来.
synchronized(this){
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() + "正在发售第 " + tickets-- +"张票.");
}else{
break;
}
}
}
解决方式二:同步方法
在有可能会发生安全问题的方法修饰符后加上synchronized关键字
@Override
public synchronized void run() {
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() + "正在发售第 " + tickets-- +"张票.");
}else{
break;
}
}
}
死锁的产生
public class Thread06 {
public static void main(String[] args) throws InterruptedException {
Dead dead = new Dead();
Thread t1 = new Thread(dead,"第一个线程");
Thread t2 = new Thread(dead,"第二个线程");
t1.start();
Thread.sleep(2);
//更换标记 使线程进入else内
dead.flag = false;
t2.start();
}
}
class Dead implements Runnable{
boolean flag = true;
//创建两把锁
Object o1 = new Object();
Object o2 = new Object();
@Override
public void run() {
if(flag == true){
while(true){
synchronized(o1){
System.out.println(Thread.currentThread().getName()+"----在执行if while 循环");
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"----在执行if while 循环");
}
}
}
}else{
while(true){
synchronized(o2){
System.out.println(Thread.currentThread().getName()+"----在执行else while 循环");
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"----在执行else while 循环");
}
}
}
}
}
}