Oracle培训(十三)——Core Java第十三章知识点总结——线程
Core Java第十三章知识点总结——线程
知识点预览
线程的概念
线程的开发
线程的状态
线程的同步
wait与notify
1. 线程的概念
在一个程序中同时运行的多个独立流程,每一个独立的流程就是一个线程。
2. 线程的三要素:CPU Code Data
3. 线程并发
同一时间多个线程同时执行
多个线程:轮流使用CPU,有先后顺序,
短暂时间:CPU时间片
人的反映时间远大于CPU时间片:认为线程同时执行
4. 主线程
main方法代表主线程
线程的开发
1.继承Thread类与实现Runnable接口两种方式
2. 继承Thread类开发线程
a) 用户定义一个类继承Thread类
b) 覆盖run方法
c) 运行线程//start 启动线程
3. 思考
package chp13.ex01;
/**
*
* @Author: Wentasy
* @FullName: TestThread.java
* @Description: 线程的创建
* @Create Date: 2012-8-17
*/
class MyThread1 extends Thread{
public void run(){
for(int i = 1; i<=1000; i++){
System.out.println(i + " $$$");
}
}
}
class MyThread2 extends Thread{
public void run(){
for(int i = 1; i<=1000; i++){
System.out.println(i + " ###");
}
}
}
public class TestThread{
public static void main(String args[]){
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
//启动线程
t1.start();
t2.start();
}
}
a) 程序的输出结果固定吗?
不是运行需要CPU分配时间片
b) 程序中存在几个线程?程序的先后顺序
3个线程(t1,t2,主线程)没有关系独立的
main----->t1、t2、main(无顺序竞争关系谁先执行完不确定)
c) 可不可以直接在main方法中直接调用run()
不可以,主线程调用run后,不是3个线程,只有一个主线程,相当于调用方法,线程没有启动。
4. Runnable接口开发线程
a)用户开发一个类实现Runnable接口
b)实现run()
c) 运行线程
Runnable target = newMyRunnable2();
Thread t2 = new Thread(target);
5. 两种建立线程方式的比较
a) 继承Thread是面向对象的编程方式
b) 实现Runnable接口解决了单一继承限制
线程的状态
1.Thread a = new Thread(); a.start();
a) 初始状态:创建了线程对象
b) 可运行状态:调用完了start()
c)运行状态:可运行状态下的线程获得CPU时间片
d) 终结状态:run方法内容全部执行完
2. sleep与阻塞
阻塞状态
3.sleep方法(Thread定义)
a) public static void sleep(longmillis) throws InterruptedException
// long millis:睡多少毫秒
// InterruptedException:检查异常
b) 利用sleep()方法对线程的控制是非常不精确的。
4.join方法
a)join方法法也会导致线程阻塞
b)特点:如果当前线程中调用了另外一个线程的join方法,当前线程会立即阻塞着,直到另外一个线程运行完成。
c) join方法的问题:
i.如果2个线程调用对方的join方法会导致程序无法运行
ii.解决办法:public final void join(long millis) throws InterruptedException
//重载方法
//参数为long类型往往代表毫秒
//不管是否运行完,阻塞------>可运行
线程同步
1. 应用数组实现一个栈
package chp13.ex05;
/**
*
* @Author: Wentasy
* @FullName: TestMyStack.java
* @Description: 用数组实现一个栈
* @Create Date: 2012-8-17
*/
class MyStack{
char[] data = {'A', 'B', ' '};
int index = 2;
public void push(char ch){
data[index] = ch;
index ++;
}
public void pop(){
index --;
data[index] = ' ';
}
public void print(){
for(int i = 0; i<data.length; i++){
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
public class TestMyStack{
public static void main(String args[]){
MyStack ms = new MyStack();
ms.push('C');
ms.print();// A B C
ms.pop();
ms.print();// A B
}
}
a) pop:弹出
b) push:压入
c) 代码实现没有问题
d) 改动的MyStack中的push方法 Sleep
e) 改写代码提供两个线程 一个存值 一个取值
f) 数据不一致
2.产生数据不一致原因
多个线程并发访问了同一个对象,如果破坏了不可分割的操作,从而就会造成数据不一致。
3. 临界资源
被多个线程并发访问的对象
4. 原子操作
不可分割的操作
5.线程不安全的对象
被多个线程并发访问时,如果一个对象有可能出现数据不一致的问题,那么这个对象称为线程不安全对象 ArrayList:线程不安全 Vector:线程安全
6. 如何解决多线程并发访问的问题
synchronized(object){
代码块
//object:任何对象
}
互斥锁标志:每个对象都有
synchronized(this)(使用当前对象的互斥锁标志)
synchronized修饰方法(使用当前对象的互斥锁标志)
7. synchronized注意
a) 对象互斥锁标志是与对象挂钩的
synchronized(obj1){
代码块1;
}
synchronized(obj1){
代码块2;
}
synchronized(obj2){
代码块3;
}
b)死锁
synchronized(a){
...//1
synchronized(b){
}
}
synchronized(b){
...//2
synchronized(a){
}
}
wait与notify(Object)
1. 用于解决死锁
synchronized(a){
...//1
a.wait();
synchronized(b){
}
}
synchronized(b){
...//2
synchronized(a){
a.notify();
}
}
2.线程通信
生产者和消费者问题
同时两个线程操作一个栈,一个线程负责往栈中添加数据,另一个线程负责从栈中删除数据。
package chp13.ex09;
/**
*
* @Author: Wentasy
* @FullName: TestProducerConsumer.java
* @Description: 线程通信:生产者和消费者问题
* @Create Date: 2012-8-17
*/
public class TestProducerConsumer {
public static void main(String[] args) {
Stack s=new Stack();
Thread t1=new Producer(s);
Thread t2=new Consumer(s);
t1.start();
t2.start();
}
}
class Stack{
private char[] data=new char[6];
private int index=0;
private void print(){
for(int i=0;i<index;i++){
System.out.print(data[i]+" ");
}
System.out.println();
}
public synchronized void push(char c){
while(index==data.length){
try {
this.wait();
} catch (InterruptedException e) {}
}
data[index]=c;
index++;
this.notifyAll();
System.out.print(c+" push stack:");
print();
}
public synchronized void pop(){
while(index==0){
try {
this.wait();
} catch (InterruptedException e) {}
}
index--;
char c=data[index];
data[index]=' ';
this.notifyAll();
System.out.print(c+" poped stack:");
print();
}
}
class Producer extends Thread{
Stack s;
public Producer(Stack s) {
this.s = s;
}
public void run(){
for(char c='A';c<='Z';c++){
s.push(c);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
Stack s;
public Consumer(Stack s) {
this.s = s;
}
public void run(){
for(int i=1;i<=26;i++){
s.pop();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
栈满:不能加
栈空:不能弹