线程:
程序:
进程:正在内存中运行的程序
线程:进程中并发运行的的独立过程,是可以单独执行的过程。
java将操作系统复杂的线程机制封装到了Thread类。
java如何开启线程:
1 继承Thread
2 重写run提供独立运行的过程
3 创建线程对象
4 调用Thread提供start方法,将run提交给操作系统,由操作系统调度并执行
5 操作系统独立的执行run方法
当前线程:
是被并发调用的,获取正在执行当前方法的线程,可以使用Thread.crrentThread()
java启动以后启动主线程,主线程执行了main方法,在main方法中,执行的一系列方法都是被主线程调用的方法。
使用Runnable接口创建线程
Runnable中有方法run,就是线程中执行的任务方法
1 实现Runnable接口中的run方法
2 创建Thread对象,以Runnable接口对象作为参数(提供run方法)
注:使用Runnable创建线程:线程的执行方法与线程对象分离了,使用更加方便灵活
后台线程:
java 进程结束条件:当全部的前台线程都结束时候,java进程结束
如果有没有结束的后台线程,这些线程被提前结束。
垃圾回收器,就是利用后台线程进行垃圾回收的
获取垃圾回收器线程的方法:
/**
* 不建议重写finalize()方法,但是为了获取执行该方法的线程,所以任性一下
* 1 是java在回收对象内存空间时候调用的方法
* 2 被垃圾回收器调用的方法
* 3如果重写必须使用super.finalize();调用Object的
* finalize()方法
* */
@Override
protected void finalize() throws Throwable {
super.finalize();
Thread t=Thread.currentThread();
System.out.println(t.getId()+" "+t.getName());
System.out.println(t.getPriority());
//显示这个对象的类型
System.out.println(t.getClass().getName());
}
线程的协调工作:
1 是可以使用sleep协调两个线程直接的等待关系
2 使用join连接两个线程:一个线程等待另外一个线程结束再执行(等待的线程被打断时会抛出InterruptedException)
final 声明的变量被初始化一次不能再改变了
final 声明的引用变量的指向不能改了,但是所指的对象中的内容是可以改变的。
在JDK1.8之前 在内部类中使用局部变量,需要将局部变量声明为final类型
IO阻塞方法:read readInt readLine
线程并发安全:
public class Demo13 {
public static void main(String[] args) {
final House house = new House();
house.addToy("Tom");
house.addToy("Jerry");
Thread t1 = new Thread() {
@Override
public void run() {
try {
house.showToys();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
house.addToy("Nemo");
}
};
t1.start();
t2.start();
}
}
class House {
List<String> toys = new ArrayList<String>();
List<String> foods = new ArrayList<String>();
public House() {
// TODO Auto-generated constructor stub
}
public void addToy(String toy) {
synchronized (toys) {
toys.add(toy);
}
}
public void showToys() throws InterruptedException {
synchronized (toys) {
for (String t : toys) {
System.out.println(t + " ");
Thread.sleep(10);
}
}
}
public void addFood(String food) {
synchronized (foods) {
foods.add(food);
}
}
public void showFoods() throws InterruptedException {
synchronized (foods) {
for (String t : foods) {
System.out.println(t + " ");
Thread.sleep(10);
}
}
}
}
线程间协作
1 尽量使用单线程处理
2 必要时候再使用多线程
3协作方式 sleep+打断 join
同步代码块 wait notify
经典解决生产者消费者问题,不用了 阻塞队列方式 解决生产者消费者的问题 方便 JAVA 5
public class Demo15 {
public static void main(String[] args) {
final BlockingQueue<Double> queue = new LinkedBlockingQueue<Double>(1);
DemoFrame frame = new DemoFrame();
frame.setVisible(true);
// Thread t=new Thread(frame);
// t.start();
for (int i = 0; i < 100; i++) {
double p = i / 100.0;
try {
queue.put(p);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// synchronized (frame) {
// double p = i / 100.0;
// frame.setPercent(p);
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
}
frame.setVisible(false);
System.exit(0);
}
}
class DemoFrame extends JFrame implements Runnable {
private JProgressBar bar;
private JPanel panel;
private double percent;
public DemoFrame() {
bar = new JProgressBar();
panel = new JPanel(new BorderLayout());
panel.add(BorderLayout.SOUTH, bar);
this.add(panel);
this.setSize(400, 300);
this.setLocationRelativeTo(null);
this.setUndecorated(true);
this.setAlwaysOnTop(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void setVisible(boolean b) {
super.setVisible(b);
if (b) {
Thread t = new Thread(this);
t.start();
}
}
public void setPercent(double percent) {
this.percent = percent;
}
@Override
public void run() {
while (isVisible()) {
}
// while (isVisible()) {
// synchronized (this) {
// int n = (int) (percent * 100);
// bar.setValue(n);
// this.notify();
// try {
// this.wait(2000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// }
}
}
缓冲队列
阻塞队列
添加数据的方法
1 add(数据) 添加成功返回true,如果满添加失败抛出异常
2 offer(数据) 添加成功返回true, 如果满了添加失败返回false
3 put(数据) 如果没满直接插入,如果满了就阻塞,直到添加成功为止
4 offer(数据,等待时间,时间单位) 如果没满直接插入,如果满了就等待指定时间,
超时还没有添加成功,就返回false,等待期间插入成功返回true;
3与4被中断是抛出中断异常
获取数据的方法
1 remove() 成功就取出数据,队列空抛异常
2 poll() 成功就取出数据,队列空返回null
3 take() 成功就取出数据,队列空阻塞到有数据位置
4 poll(等待时间,时间单位)成功就取出数据,队列空就阻塞,在阻塞
期间取到数据就返回数据,否则等待时间到也没有得到数据就返回null
检查数据方法(不取出数据)
peek() 缓冲队列
阻塞队列
添加数据的方法
1 add(数据) 添加成功返回true,如果满添加失败抛出异常
2 offer(数据) 添加成功返回true, 如果满了添加失败返回false
3 put(数据) 如果没满直接插入,如果满了就阻塞,直到添加成功为止
4 offer(数据,等待时间,时间单位) 如果没满直接插入,如果满了就等待指定时间,
超时还没有添加成功,就返回false,等待期间插入成功返回true;
3与4被中断是抛出中断异常
获取数据的方法
1 remove() 成功就取出数据,队列空抛异常
2 poll() 成功就取出数据,队列空返回null
3 take() 成功就取出数据,队列空阻塞到有数据位置
4 poll(等待时间,时间单位)成功就取出数据,队列空就阻塞,在阻塞
期间取到数据就返回数据,否则等待时间到也没有得到数据就返回null
检查数据方法(不取出数据)
peek() 缓冲队列
阻塞队列
添加数据的方法
1 add(数据) 添加成功返回true,如果满添加失败抛出异常
2 offer(数据) 添加成功返回true, 如果满了添加失败返回false
3 put(数据) 如果没满直接插入,如果满了就阻塞,直到添加成功为止
4 offer(数据,等待时间,时间单位) 如果没满直接插入,如果满了就等待指定时间,
超时还没有添加成功,就返回false,等待期间插入成功返回true;
3与4被中断是抛出中断异常
获取数据的方法
1 remove() 成功就取出数据,队列空抛异常
2 poll() 成功就取出数据,队列空返回null
3 take() 成功就取出数据,队列空阻塞到有数据位置
4 poll(等待时间,时间单位)成功就取出数据,队列空就阻塞,在阻塞
期间取到数据就返回数据,否则等待时间到也没有得到数据就返回null
检查数据方法(不取出数据)
peek() 缓冲队列
阻塞队列
添加数据的方法
1 add(数据) 添加成功返回true,如果满添加失败抛出异常
2 offer(数据) 添加成功返回true, 如果满了添加失败返回false
3 put(数据) 如果没满直接插入,如果满了就阻塞,直到添加成功为止
4 offer(数据,等待时间,时间单位) 如果没满直接插入,如果满了就等待指定时间,
超时还没有添加成功,就返回false,等待期间插入成功返回true;
3与4被中断是抛出中断异常
获取数据的方法
1 remove() 成功就取出数据,队列空抛异常
2 poll() 成功就取出数据,队列空返回null
3 take() 成功就取出数据,队列空阻塞到有数据位置
4 poll(等待时间,时间单位)成功就取出数据,队列空就阻塞,在阻塞
期间取到数据就返回数据,否则等待时间到也没有得到数据就返回null
检查数据方法(不取出数据)
peek() 缓冲队列
阻塞队列
添加数据的方法
1 add(数据) 添加成功返回true,如果满添加失败抛出异常
2 offer(数据) 添加成功返回true, 如果满了添加失败返回false
3 put(数据) 如果没满直接插入,如果满了就阻塞,直到添加成功为止
4 offer(数据,等待时间,时间单位) 如果没满直接插入,如果满了就等待指定时间,
超时还没有添加成功,就返回false,等待期间插入成功返回true;
3与4被中断是抛出中断异常
获取数据的方法
1 remove() 成功就取出数据,队列空抛异常
2 poll() 成功就取出数据,队列空返回null
3 take() 成功就取出数据,队列空阻塞到有数据位置
4 poll(等待时间,时间单位)成功就取出数据,队列空就阻塞,在阻塞
期间取到数据就返回数据,否则等待时间到也没有得到数据就返回null
检查数据方法(不取出数据)
peek()
public class Demo01 {
public static void main(String[] args) {
final BlockingQueue<String> queue = new LinkedBlockingQueue<String>(3);
// queue.add("Tom");
// queue.add("Jerry");
// queue.add("Nemo");
Thread t1 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("插入前" + queue);
System.out.println("开始插入数据");
queue.put("John");
System.out.println("插入后" + queue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
System.out.println("开始取出数据");
String name = queue.take();
System.out.println("取出了:" + name);
System.out.println("取出了" + queue);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t1.start();
t2.start();
}
}
/**
* 线程池
*
* ExecutorService执行器服务 Executors 工厂
*
* @author tarena
*
*/
public class Demo03 {
// @Test
public void test() throws InterruptedException {
// 尽量重用已经存在的线程,如果不存在就创建新的
// 但是旧线程使用以后,还会加以重用
// ExecutorService p1=Executors.newCachedThreadPool();
// 创建两个任务(两个run方法),先提交一个任务,当一个任务结束以后,我再提交第二个任务
// 先后提交任务使用了同一个线程,执行的!
// execute将一个任务提交到线程池,由线程池派发线程池执行
// p1.execute(new Task());
// Thread.sleep(1000);
// p1.execute(new Task());
}
// @Test
public void testNewFixedThreadPool() {
/*
* 最多重用3个线程,执行任务,更多任务将等到执行
*/
ExecutorService p2 = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
p2.execute(new Task());
}
}
// @Test
public void testSingleThread() {
/**
* 只重用一个线程,执行任务,更多代码将等待
*/
ExecutorService pool=Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
pool.execute(new Task());
}
}
@Test
public void test1() throws InterruptedException{
/**
* 利用重用的线程,定时执行计划任务,控制线程总数
*/
ScheduledExecutorService pool=
Executors.newScheduledThreadPool(4);
//一次性执行任务
pool.schedule(new Task(), 2,TimeUnit.SECONDS);
//定期执行任务
pool.scheduleAtFixedRate(new Task(), 0,1,TimeUnit.SECONDS);
Thread.sleep(10000);
}
}
class Task implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println(t.getName() + "," + t.getId());
}
}
/**
* ThreadLocal
* 属于线程的变量
*
* 局部变量:在方法中声明,离开作用域就销毁的变量,在栈中分配。局部变量也是临时变量。
* 每个线程都有一个栈。(每个线程都有自己的栈)
* 实例变量:对象属性,是属于对象的变量,在堆中分配,多个线程都可以共享同一个对象。
* 属于线程的变量:ThreadLocal,是存储在线程上的变量。可以利用这个变量,
* 在同一个线程调用一系列方法时共享数据。这些数据存储在线程对象中的一个Map集合(每一个线程都会维护一个Map集合)中。
* @author tarena
*
*/
public class Demo06 {
public static void main(String[] args) {
final Shop shop=new Shop();
final int s=0;
Thread p1=new Thread(){
@Override
public void run() {
try {
int e=s;
Account.setName("Tom");
shop.getMoney();
Thread.sleep(1000);
shop.getProduct();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread p2=new Thread(){
@Override
public void run() {
try {
Account.setName("Jerry");
shop.getMoney();
Thread.sleep(1000);
shop.getProduct();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
p1.start();
p2.start();
}
}
class Account{
private static ThreadLocal<String> name=new ThreadLocal<String>(){
protected String initialValue() {
return "NO Name";
}
};
public static synchronized void setName(String name){
String n=Account.name.get();
if(n==null){
n=name;
Account.name.set(name);
}
}
public static synchronized String getName(){
/*
* 为了保证创建唯一的一个对象,避免因为并发线程重复创建对象
* 经常利用同步关键字,保证代码的原子性,在执行期间不会发生并发访问的问题
*/
String n=Account.name.get();
if(n==null){
Account.name.set("佚名");
}
return n;
}
}
class Shop{
public void getMoney(){
String name=Account.getName();
Thread t=Thread.currentThread();
System.out.println("线程"+t.getName()+
"被用户:"+name+"使用去取钱了");
}
public void getProduct(){
String name=Account.getName();
Thread t=Thread.currentThread();
System.out.println("线程"+t.getName()+
"被用户:"+name+"使用去购物了");
}
}