java實(shí)現(xiàn)多線程有兩種方法:
一種是繼承Thread類
如下面例子:
public class TestThread extends Thread {
//實(shí)現(xiàn)多線程方法一:繼承Thread類,并重載run方法
public void run(){
//要將一段代碼在一個(gè)新的線程上運(yùn)行,該代碼應(yīng)寫在run函數(shù)中
while(true){
System.out.println(Thread.currentThread().getName() + "is running...");
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
//啟動(dòng)一個(gè)新的線程,不是直接調(diào)用Thread類子類對(duì)象的run方法,
//而是調(diào)用Thread類子類對(duì)象的start方法,Thread類對(duì)象的start方法
//將產(chǎn)生新的線程,并在該線程上運(yùn)行該Thread類對(duì)象中的run方法,根據(jù)
//多態(tài)性 實(shí)際上運(yùn)行的是Thread子類的run方法
TestThread testThread = new TestThread();
testThread.start();
while(true){
System.out.println("main thread is running...");
}
}
}
方法二是:實(shí)現(xiàn)接口Runnable中的run函數(shù)
如下列所示
//實(shí)現(xiàn)多線程方法二:實(shí)現(xiàn)Runnable接口,重載run函數(shù),
//大多數(shù)情況下,如果只想重寫 run() 方法,而不重寫其他 Thread 方法,
//那么應(yīng)使用 Runnable 接口。這很重要,
//因?yàn)槌浅绦騿T打算修改或增強(qiáng)類的基本行為,否則不應(yīng)為該類創(chuàng)建子類。
public class TestThread implements Runnable {
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "is running......");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TestThread testThread = new TestThread();//創(chuàng)建TestThread類的一個(gè)實(shí)例
Thread thread = new Thread(testThread);//創(chuàng)建一個(gè)Thread類的實(shí)例
thread.start();//使線程進(jìn)入runnable狀態(tài)
while(true){
System.out.println("main thread is running...");
}
}
2.這兩種方法的區(qū)別:實(shí)現(xiàn)Runnable接口適合多個(gè)相同的程序代碼訪問(wèn)處理同一資源
如下列所示:
//四個(gè)售票窗口售同一中車票,總共有100張,要求這四個(gè)售票多線程進(jìn)行
public class TicketRunnable implements Runnable {
private int tickets = 100;
public void run() {
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
public class TicketRunnableDemo {
public static void main(String[] args){
//創(chuàng)建了四個(gè)線程,每個(gè)線程調(diào)用了同一各TicketRunnable對(duì)象中run方法,
//訪問(wèn)的是同一個(gè)對(duì)象中的變量tickets,從而滿足了我們的要求。
//實(shí)現(xiàn)Runnable接口適合多個(gè)相同的程序代碼訪問(wèn)處理同一資源
TicketRunnable ticketrunnable = new TicketRunnable();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
new Thread(ticketrunnable).start();
}
}
而繼承繼承Thread類,并重載run方法不能達(dá)到此目的
如下所示:
public class TicketThread extends Thread {
private int tickets = 100;
public void run(){
while(true){
if(tickets>0){
System.out.println(Thread.currentThread().getName() +
"is saling ticket " + tickets--);
}
}
}
}
public class TicketThreadDemo {
public static void main(String[] args){
//我們希望的是四個(gè)線程共售100,我們創(chuàng)建四個(gè)線程
//但結(jié)果與我們希望的相反,各個(gè)線程各售100張票
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
// 如果我們象下面這樣寫的話,只有一個(gè)線程在售票,沒(méi)有實(shí)現(xiàn)四個(gè)線程并發(fā)售票
// TicketThread ticketThread = new TicketThread();
// ticketThread.start();
// ticketThread.start();
// ticketThread.start();
// ticketThread.start();
}
}
3.后臺(tái)線程
public class DaemonTest {
public static void main(String[] args){
TicketRunnable ticketRunnable = new TicketRunnable();
Thread thread = new Thread(ticketRunnable);
thread.setDaemon(true);//將此線程設(shè)置為后臺(tái)線程,當(dāng)進(jìn)程中只有后臺(tái)線程時(shí),進(jìn)程就會(huì)結(jié)束
thread.start();
}
}
4.聯(lián)合線程
(還沒(méi)搞懂)
5. 線程同步
在上面的售票程序中,線程可能是不安全的,
比如我們修改一下:
public class ThreadRunnable implements Runnable{
private int tickets = 100;
public void run() {
while(true){
if(tickets > 0){
try{
Thread.sleep(10);//使該線程暫停,
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
再次測(cè)試時(shí),發(fā)現(xiàn)可能會(huì)打印-1 -2 這樣的ticket
所以我們要修改一下,使該程序是線程安全的,
public class TicketSynch implements Runnable{
private int tickets = 100;
String str = new String("");//此對(duì)象放在run函數(shù)之外
public void run(){
while(true){
synchronized(str){//同步代碼塊
if(tickets > 0){
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+" is saling ticket " + tickets--);
}
}
}
}
}
但是 同步以犧牲程序的性能為代價(jià)
5. 同步函數(shù)
public class SynchMethod implements Runnable{
private int tickets = 100;
public void run(){
while(true){
sale();
}
}
public synchronized void sale(){
if(tickets > 0){
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+ "is saling ticket " + tickets--);
}
}
}
public static void main(String[] args){
//創(chuàng)建了四個(gè)線程,每個(gè)線程調(diào)用了同一各TicketRunnable對(duì)象中run方法,
//訪問(wèn)的是同一個(gè)對(duì)象中的變量tickets,從而滿足了我們的要求。
//實(shí)現(xiàn)Runnable接口適合多個(gè)相同的程序代碼訪問(wèn)處理同一資源
SynchMethod synchMethod = new SynchMethod();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
new Thread(synchMethod).start();
}
6.代碼塊和函數(shù)間的同步
public class MethodAndBlockSyn implements Runnable{
private int tickets = 100;
String str = new String("");
public void run(){
if(str.equals("method")){
while(true){
sale();
}
}
else{
synchronized(str){//同步代碼塊
if(tickets > 0){
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+" is saling ticket " + tickets--);
}
}
}
}
public synchronized void sale(){
if(tickets > 0){
try{
Thread.sleep(10);
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName()
+ "is saling ticket " + tickets--);
}
}
}
public static void main(String[] args){
MethodAndBlockSyn test = new MethodAndBlockSyn();
new Thread(test).start();// 這個(gè)線程調(diào)用同步代碼塊
try{
Thread.sleep(1);//讓線程等待100ms 是為了讓該線程得到CPU
//避免該線程未得到CPU就將str設(shè)置為method,從而使此線程也是調(diào)用同步函數(shù)
}catch(Exception e)
{
}
test.str = new String("method");
new Thread(test).start();//這個(gè)線程調(diào)用同步函數(shù)
}
7. 線程間通訊
wait: 告訴當(dāng)前線程放棄監(jiān)視器并進(jìn)入睡眠狀態(tài),知道有其他線程進(jìn)入同一監(jiān)視器并調(diào)用notify為止。
notify:?jiǎn)拘淹粚?duì)象監(jiān)視器中調(diào)用wait的第一個(gè)線程。類似飯館有一個(gè)空位置后通知所有等待就餐顧客中的第一位可以入座的情況
notifyAll:?jiǎn)拘淹粚?duì)象監(jiān)視器中調(diào)用wait的所有線程,具有最高優(yōu)先級(jí)的程序先被喚醒并執(zhí)行。
以上三個(gè)方法只能在synchronized方法中調(diào)用
public class Record {
private String name = "陳世美";
private String sex = "女";
boolean bFull = false;
public synchronized void put(String name, String sex)
{
if(bFull)
try {
wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.name = name;
try{
Thread.sleep(10);
}
catch(Exception e){
System.out.println(e.getMessage());
}
this.sex = sex;
bFull = true;
notify();
}
public synchronized void get(){
if(!bFull)
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name + "--->" + sex);
bFull = false;
notify();
}
}
public class Productor implements Runnable {
Record record = null;
public Productor(Record record){
this.record = record;
}
public void run(){
int i = 0;
while(true){
if(i==0)
record.put("陳陳陳", "男");
else
record.put("陳世美", "女");
i = (i+1)%2;
}
}
}
public class Consumer implements Runnable {
Record record = null;
public Consumer(Record record){
this.record = record;
}
public void run(){
while(true){
record.get();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Record record = new Record();
new Thread(new Productor(record)).start();
new Thread(new Consumer(record)).start();
}