并发与多线程,
分享于 点击 13063 次 点评:278
并发与多线程,
并发与多线程
基本概念
并发与并行
进程与线程
线程
创建线程
Thread
Runnable
多个 Thread 实例共用一个 Runnable,这些线程的 run 方法相同,可以共享相同的数据
但是存在线程同步问题
public class RunnableTest implements Runnable
{
private int ticket = 10;
public void run()
{
while (true)
{
if (ticket > 0)
{
System.out.println(Thread.currentThread().getName() + "售出" + ticket + "号票");
ticket--;
}
else System.exit(0);
}
}
public static void main(String[] args)
{
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt, "1号窗口");
Thread t2 = new Thread(rt, "2号窗口");
t1.start();
t2.start();
}
}
多个 Thread 实例共用一个 Runnable,这些线程的 run 方法相同,可以共享相同的数据
但是存在线程同步问题
public class RunnableTest implements Runnable
{
private int ticket = 10;
public void run()
{
while (true)
{
if (ticket > 0)
{
System.out.println(Thread.currentThread().getName() + "售出" + ticket + "号票");
ticket--;
}
else System.exit(0);
}
}
public static void main(String[] args)
{
RunnableTest rt = new RunnableTest();
Thread t1 = new Thread(rt, "1号窗口");
Thread t2 = new Thread(rt, "2号窗口");
t1.start();
t2.start();
}
}
1号窗口售出10号票
1号窗口售出9号票
1号窗口售出8号票
1号窗口售出7号票
2号窗口售出7号票
2号窗口售出5号票
1号窗口售出6号票
2号窗口售出4号票
1号窗口售出3号票
2号窗口售出2号票
1号窗口售出1号票
匿名类
匿名类可以方便的访问方法的局部变量,但是必须声明为 final,因为匿名类和普通局部变量生命周期不一致
jdk7 中已不再需要显示声明为 final,实际上被虚拟机自动隐式声明了
public static void main(String[] args)
{
new Thread( )
{
public void run( )
{
//内容
}
}.start( );
new Thread(new Runnable( )
{
public void run( )
{
//内容
}
}).start( );
}
Callable
线程方法
线程状态
线程同步
保证程序原子性、可见性、有序性的过程
阻塞同步
基于加锁争用的悲观并发策略
synchronized
ReentrantLock
非阻塞同步
非阻塞同步是一种基于冲突检测和数据更新的乐观并发策略
actomic 类
无同步方案
如果一个方法不涉及共享数据,那么他天生就是线程安全的
可重入代码
可以在代码执行的任何时刻中断它,转而去执行另外一段代码,在控制权返回之后,原来的程序不会出现任何的错误
线程本地存储
死锁
死锁条件
Java死锁示例
public static void main(String[] args)
{
Object o1 = new Object( );
Object o2 = new Object( );
Thread t1 = new Thread( )
{
public void run( )
{
synchronized (o1)//占有 o1
{
System.out.println("t1 已占有 O1");
try
{
Thread.sleep(1000);//停顿1000毫秒,另一个线程有足够的时间占有 o1
}
catch (InterruptedException e)
{
e.printStackTrace( );
}
System.out.println("t1 试图占有 o2");
System.out.println("t1 等待中");
synchronized (o2)
{
System.out.println("t1 已占有 O2");
}
}
}
};
Thread t2 = new Thread( )
{
public void run( )
{
synchronized (o2) //占有 o2
{
System.out.println("t2 已占有 o2");
try
{
Thread.sleep(1000);//停顿1000毫秒,另一个线程有足够的时间占有 o2
}
catch (InterruptedException e)
{
e.printStackTrace( );
}
System.out.println("t2 试图占有 o1");
System.out.println("t2 等待中");
synchronized (o1)
{
System.out.println("t2 已占有 O1");
}
}
}
};
t1.start( );
t2.start( );
}
t1 已占有 O1
t2 已占有 o2
t1 试图占有 o2
t1 等待中
t2 试图占有 o1
t2 等待中
线程通信
public class ProducerAndConsumer
{
public static void main(String[] args)
{
Goods goods = new Goods();
Thread producer = new Thread()//生产者线程
{
public void run()
{
while (true) goods.put();
}
};
Thread consumer = new Thread()//消费者线程
{
public void run()
{
while (true) goods.take();
}
};
consumer.start();
producer.start();
}
}
class Goods//商品类
{
int num = 0;//商品数目
int space = 10;//空位总数
public synchronized void put()
{
if (num < space)//有空位可放,可以生产
{
num++;
System.out.println("放入一个商品,现有" + num + "个商品," + (space - num) + "个空位");
notify();//唤醒等待该锁的线程
}
else//无空位可放,等待空位
{
try
{
System.out.println("没有空位可放,等待拿出");
wait();//进入该锁对象的等待池
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public synchronized void take()
{
if (num > 0)//有商品可拿
{
num--;
System.out.println("拿出一个商品,现有" + num + "个商品," + (space - num) + "个空位");
notify();//唤醒等待该锁的线程
}
else///等待生产产品
{
try
{
System.out.println("没有商品可拿,等待放入");
wait();//进入该锁对象的等待池
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
没有商品可拿,等待放入
放入一个商品,现有1个商品,9个空位
放入一个商品,现有2个商品,8个空位
拿出一个商品,现有1个商品,9个空位
放入一个商品,现有2个商品,8个空位
放入一个商品,现有3个商品,7个空位
放入一个商品,现有4个商品,6个空位
拿出一个商品,现有3个商品,7个空位
放入一个商品,现有4个商品,6个空位
···
线程池
线程的启动和结束都是比较消耗时间和占用资源的,如果在系统中用到了很多的线程,大量的启动和结束动作会严重影响性能
线程池很像生产者消费者模式,消费的对象是一个一个的能够运行的任务
线程安全类
相关文章
- 暂无相关文章
用户点评