创建线程两种方法:继承Thread类或者实现Runnable接口
一:继承Thread类。
步骤:1、定义一个类继承Thread类。
2、覆盖Thread类中的run方法。
3、直接创建Thread的子类对象创建线程。
4、调用start方法开启线程并调用线程的任务run方法执行。
通过getName()方法获取线程名,默认名称:Thread-编号(从0开始)
二:实现Runnable接口。
1、定义类实现Runnable接口。2、覆盖接口的run()方法,将线程的任务代码封装到run()方法中。
3、创建Thread对象,将Runnable接口的子类对象作为Thread类构造函数的参数传递。
4、调用Thread对象的start()方法开启线程。
Thread :如果没有父类可以继承Thread
Runnable :避免了单继承的局限性,建议采用实现接口方式创建线程
线程安全问题产生原因:
1、多个线程操作共享数据时。2、操作共享数据的代码有多条代码。
在一个线程操作的过程中,其他线程可以参与进来。
线程状态转换
synchronized 同步锁:
同步代码块:锁是一个对象;同步函数:锁其实是this;
静态同步函数:锁是 类名.class 它是一个Class类的字节码文件对象
/*
卖票程序
同步代码块形式
*/
class Ticket implements Runnable { //接口没有抛出异常,所以run()方法内只能捕捉异常
private int num = 100;
Object obj = new Object();
public void run() {
while(true) {
try{
Thread.sleep(100);//---测试多线程调用时,数据num出现负数
}catch(InterruptedException e) {
System.out.println("线程出错了");
}
synchronized(obj){
if(num>0){
System.out.println(Thread.currentThread().getName() + "----- sale ----" + num--);
}
}
}
}
}
class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();//---只有一个Ticket对象
Thread t1 = new Thread(t);
//Thread t2 = new Thread(new Ticket());
//Thread t3 = new Thread(new Ticket());
//Thread t4 = new Thread(new Ticket()); //---4个Ticket对象需要使用static修饰num
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
举例:火车上的卫生间
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:有多个线程并使用同一个锁。
单例设计模式:
使类只有一个对象(构造函数私有化,在类中创建本类对象并私有化为静态,定义公共静态方法,通过调用返回对象)饿汉式:对象在类进入内存中时加载。不存在线程安全的问题
懒汉式:对象延迟加载。使用同步代码块,锁是类名.class,并在锁外在进行一次判断(双层判断)。可以防止加锁浪费资源。
/*
多线程下的单例设计模式
*/
//---饿汉式
class SingleHg {
private static final SingleHg s = new SingleHg();
private SingleHg(){}
public static SingleHg getInstance(){
return s;
}
}
//---懒汉式
class SingleLz {
private static SingleLz s = null;
private SingleLz(){}
public static SingleLz getInstance(){
if(s==null)
s = new SingleLz();
return s;
}
}
//---加同步锁的懒汉式,面试常考,一般实际用饿汉式
class Single {
private static Single s = null;
private Single(){}
public static /*synchronized*/ Single getInstance(){ //---使用同步函数会降低效率,每次都要获取锁
if(s==null)
synchronized(Single.class){
if(s==null)
s = new Single();
}
return s;
}
}
死锁程序:
Interrupt();强制清除线程的冻结状态,使线程获取执行资格,但是会使线程抛出InterruptedException线程间通信:其实是多个线程在操作同一个资源,只是操作的动作不同;
考虑同步问题,加入等待唤醒机制;wait() notify()wait();notify();notifyAll(); 这些方法都应该在同步代码中使用,对持有锁的线程进行操作。
这些方法都定义在Object类中,所有对象都可以是锁,这些方法只能唤醒或者等待持有相同锁的线程。
生产者消费者问题:
多个生产者消费者,需要用while()循环判断标记;
class Duck {
private int id;
Duck(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Duck [id=" + id + "]";
}
}
class DuckBasket {
int index = 0;
Duck[] ducks = new Duck[5];
public synchronized void push(Duck duck) {
while(index==ducks.length)
try {
System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
this.wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
ducks[index] = duck;
System.out.println(Thread.currentThread().getName()+"生产了"+duck);
index++;
notify();
}
public synchronized Duck pop() {
while(index==0)
try {
System.out.println(Thread.currentThread().getName()+":空了,快生产烤鸭!");
wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
index--;
//---打印输出用到了共享数据,要放到同步代码中
System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);
notify();
return ducks[index];
}
}
class Producer implements Runnable {
DuckBasket db;
public Producer(DuckBasket db) {
super();
this.db = db;
}
@Override
public void run() {
for(int i=0; i<20; i++) {
Duck duck = new Duck(i);
db.push(duck);
}
}
}
class Consumer implements Runnable {
DuckBasket db;
public Consumer(DuckBasket db) {
super();
this.db = db;
}
@Override
public void run() {
for(int i=0; i<20; i++) {
db.pop();
}
}
}
public class ProducerConsumerDemo {
/**
* @param args
*/
public static void main(String[] args) {
DuckBasket db = new DuckBasket();
Producer pro1 = new Producer(db);
Consumer con1 = new Consumer(db);
Thread t1 = new Thread(pro1,"生产者----1");
Thread t2 = new Thread(con1,"消费者1");
Thread t3 = new Thread(pro1,"生产者----2");
Thread t4 = new Thread(con1,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
新特性,多线程的升级解决方案,将同步的synchronized 升级为显示的lock(接口),
将wait(),notify(),notifyAll()升级为condition对象,该对象可以通过lock锁获取。
可以实现只唤醒对方的操作。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Duck {
private int id;
Duck(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Duck [id=" + id + "]";
}
}
class DuckBasket {
int index = 0;
Duck[] ducks = new Duck[5];
Lock lock = new ReentrantLock();
Condition proL = lock.newCondition();
Condition conL = lock.newCondition();
public void push(Duck duck) {
lock.lock();
try{
while(index==ducks.length)
try {
System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
proL.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
ducks[index] = duck;
System.out.println(Thread.currentThread().getName()+"做了"+duck);
index++;
conL.signal();
}finally{
lock.unlock();
}
}
public Duck pop() {
lock.lock();
try {
while(index==0)
try {
System.out.println(Thread.currentThread().getName()+":空了,快做烤鸭!");
conL.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
index--;
//---打印输出用到了共享数据,要放到同步代码中
System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);
proL.signal();
return ducks[index];
} finally{
lock.unlock();
}
}
}
class Producer implements Runnable {
DuckBasket db;
public Producer(DuckBasket db) {
super();
this.db = db;
}
@Override
public void run() {
for(int i=0; i<20; i++) {
Duck duck = new Duck(i);
db.push(duck);
}
}
}
class Consumer implements Runnable {
DuckBasket db;
public Consumer(DuckBasket db) {
super();
this.db = db;
}
@Override
public void run() {
for(int i=0; i<20; i++) {
db.pop();
}
}
}
public class ProConLock {
/**
* @param args
*/
public static void main(String[] args) {
DuckBasket db = new DuckBasket();
Producer pro1 = new Producer(db);
Consumer con1 = new Consumer(db);
Thread t1 = new Thread(pro1,"生产者----1");
Thread t2 = new Thread(con1,"消费者1");
Thread t3 = new Thread(pro1,"生产者----2");
Thread t4 = new Thread(con1,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();
}
如何停止线程?
1、控制循环,做一个标记,使标记改变时满足条件就停止循环,结束run()方法;2、如果线程处于等待(冻结)状态,无法结束线程,可以使用interrupt()方法强制解除冻结状态,进入运行状态,这样就可以操作标记,结束线程。
wait()和sleep()相同点,都可以使线程进入冻结状态,都抛出异常。
区别:1、wait()为Object的方法,sleep()为Thread的方法
2、wait()必须有锁
3、wait()需要notify()唤醒,sleep()时间到了自动获取CPU执行资格
其他方法
join()方法,当a线程执行到了b线程的join方法时,a等待b执行完在继续执行。setDaemon();守护进程,前台结束就结束
Thread类的 toString()方法,返回 [程名称,优先级,线程组]
setPriority()设置优先级 MAX_PRIORITY MIN_PRIORITY NORMAL_PRIORITY setName()
yield();静态方法:暂停当前线程执行
匿名内部类创建进程---常用。
------- android培训、java培训、期待与您交流! ----------