欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Oracle培训(十三)——Core Java第十三章知识点总结——线程

程序员文章站 2024-01-22 21:40:34
...

Core Java第十三章知识点总结——线程


知识点预览


线程的概念

线程的开发

线程的状态

线程的同步

wait与notify


1. 线程的概念


在一个程序中同时运行的多个独立流程,每一个独立的流程就是一个线程。


2. 线程的三要素:CPU Code Data


Oracle培训(十三)——Core Java第十三章知识点总结——线程

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个线程(t1t2,主线程)没有关系独立的

main----->t1t2main(无顺序竞争关系谁先执行完不确定)


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方法内容全部执行完


Oracle培训(十三)——Core Java第十三章知识点总结——线程

2. sleep与阻塞

阻塞状态

Oracle培训(十三)——Core Java第十三章知识点总结——线程

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:任何对象

}

互斥锁标志:每个对象都有

Oracle培训(十三)——Core Java第十三章知识点总结——线程

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();
			}
		}
	}
}


栈满:不能加

栈空:不能弹